---
title: "所有权与内存模型"
description: "深入学习 Rust 的所有权系统、借用规则和内存管理，理解与 JavaScript 垃圾回收的差异"
---

# 所有权与内存模型

## 📖 学习目标

理解 Rust 的所有权系统，这是 Rust 最核心的概念。我们将从 JavaScript 的内存管理出发，逐步理解 Rust 如何通过编译时检查确保内存安全。

---

## 🎯 内存管理对比

### JavaScript 的垃圾回收

JavaScript 使用自动垃圾回收机制：

<UniversalEditor title="JavaScript 内存管理" compare={true}>
```javascript !! js
// JavaScript 内存管理示例
function createUser() {
    let user = {
        name: "Alice",
        age: 25,
        preferences: {
            theme: "dark",
            language: "zh-CN"
        }
    };
    
    // 创建对 user 的引用
    let userRef = user;
    
    // 修改 user 对象
    user.age = 26;
    console.log(userRef.age); // 26 - 引用指向同一个对象
    
    // 创建新对象，userRef 指向新对象
    userRef = {
        name: "Bob",
        age: 30
    };
    
    // 原来的 user 对象仍然存在
    console.log(user.name); // "Alice"
    console.log(userRef.name); // "Bob"
    
    // 当函数结束时，如果没有引用指向对象，垃圾回收器会清理内存
}

createUser();

// 数组和对象的引用传递
let numbers = [1, 2, 3];
let numbersCopy = numbers; // 复制引用，不是数据

numbersCopy.push(4);
console.log(numbers); // [1, 2, 3, 4] - 同一个数组
console.log(numbersCopy); // [1, 2, 3, 4] - 同一个数组

// 深拷贝
let deepCopy = JSON.parse(JSON.stringify(numbers));
deepCopy.push(5);
console.log(numbers); // [1, 2, 3, 4] - 原数组不变
console.log(deepCopy); // [1, 2, 3, 4, 5] - 新数组
```
</UniversalEditor>

### Rust 的所有权系统

Rust 使用所有权系统在编译时管理内存：

<UniversalEditor title="Rust 所有权系统" compare={true}>
```rust !! rs
// Rust 所有权系统示例
fn create_user() {
    let user = User {
        name: String::from("Alice"),
        age: 25,
        preferences: Preferences {
            theme: String::from("dark"),
            language: String::from("zh-CN"),
        },
    };
    
    // 所有权转移：user 的所有权移动到 user_ref
    let user_ref = user;
    
    // 这行代码会编译错误！user 已经被移动
    // println!("{}", user.name); // 错误：use of moved value
    
    // 可以修改 user_ref
    let mut user_ref = user_ref;
    user_ref.age = 26;
    println!("{}", user_ref.age); // 26
    
    // 创建新的 User
    let new_user = User {
        name: String::from("Bob"),
        age: 30,
        preferences: Preferences {
            theme: String::from("light"),
            language: String::from("en-US"),
        },
    };
    
    // user_ref 指向新对象
    let user_ref = new_user;
    println!("{}", user_ref.name); // "Bob"
}

// 定义结构体
struct User {
    name: String,
    age: u32,
    preferences: Preferences,
}

struct Preferences {
    theme: String,
    language: String,
}

// 向量（类似 JavaScript 数组）的所有权
fn vector_ownership() {
    let mut numbers = vec![1, 2, 3];
    let numbers_ref = &mut numbers; // 可变借用
    
    numbers_ref.push(4);
    println!("{:?}", numbers); // [1, 2, 3, 4]
    
    // 创建深拷贝
    let deep_copy = numbers.clone();
    println!("{:?}", deep_copy); // [1, 2, 3, 4]
    
    // 修改原数组
    numbers.push(5);
    println!("{:?}", numbers); // [1, 2, 3, 4, 5]
    println!("{:?}", deep_copy); // [1, 2, 3, 4] - 深拷贝不变
}

fn main() {
    create_user();
    vector_ownership();
}
```
</UniversalEditor>

### 内存管理差异

1. **自动 vs 手动**: JavaScript 自动垃圾回收，Rust 编译时检查
2. **引用语义**: JavaScript 对象通过引用传递，Rust 通过所有权转移
3. **内存安全**: JavaScript 运行时检查，Rust 编译时保证
4. **性能**: JavaScript 有垃圾回收开销，Rust 零成本抽象

---

## 🔄 所有权规则

### Rust 的所有权规则

Rust 有三个核心所有权规则：

<UniversalEditor title="所有权规则" compare={true}>
```rust !! rs
// 规则 1: 每个值都有一个所有者
// Rust 中的数据分为两种：
// 1. 拥有所有权的数据 (如 String, Vec): 默认行为是“移动” (move)。当所有权转移后，原变量失效。
// 2. 实现了 Copy Trait 的数据 (如整数、浮点数、布尔值、字符、固定大小数组): 默认行为是“复制” (copy)。复制后，原变量仍然有效。
fn rule_one() {
    // String 类型：默认行为是移动
    let s1 = String::from("hello"); // s1 是所有者
    let s2 = s1; // 所有权从 s1 移动到 s2
    // println!("{}", s1); // 错误：s1 已经被移动，不能再使用
    println!("{}", s2); // 正确：s2 现在是所有者

    // i32 类型：实现了 Copy Trait，默认行为是复制
    let x = 5; // x 是所有者
    let y = x; // x 的值被复制给 y，x 仍然有效
    println!("x = {}, y = {}", x, y); // 正确：x 和 y 都可以使用
}

// 规则 2: 同一时间只能有一个所有者
fn rule_two() {
    let s1 = String::from("hello");
    let s2 = s1; // s1 的所有权移动到 s2
    // let s3 = s1; // 错误：s1 已经被移动
    let s3 = s2; // 正确：s2 的所有权移动到 s3
}

// 规则 3: 当所有者离开作用域时，值被丢弃
fn rule_three() {
    {
        let s = String::from("hello"); // s 进入作用域
        // 使用 s
        println!("{}", s);
    } // s 离开作用域，内存被释放
    // println!("{}", s); // 错误：s 已经被释放
}

// 函数中的所有权
fn take_ownership(some_string: String) {
    println!("{}", some_string);
} // some_string 离开作用域，内存被释放

fn make_copy(some_integer: i32) {
    println!("{}", some_integer);
} // some_integer 离开作用域，但 i32 是 Copy 类型，不需要释放

fn main() {
    rule_one();
    rule_two();
    rule_three();
    
    let s = String::from("hello");
    take_ownership(s); // s 的所有权移动到函数中
    // println!("{}", s); // 错误：s 已经被移动
    
    let x = 5;
    make_copy(x); // x 被复制到函数中
    println!("{}", x); // 正确：x 仍然有效
}
```
</UniversalEditor>

---

## 🔗 借用与引用

### 不可变借用

<UniversalEditor title="不可变借用" compare={true}>
```rust !! rs
// 不可变借用（类似 JavaScript 的只读引用）
fn calculate_length(s: &String) -> usize {
    s.len() // 返回字符串长度
} // s 离开作用域，但因为它只是借用，不会释放内存

fn main() {
    let s1 = String::from("hello");
    let len = calculate_length(&s1); // 传递 s1 的引用
    println!("'{}' 的长度是 {}", s1, len); // s1 仍然有效
    
    // 多个不可变借用
    let s = String::from("hello");
    let r1 = &s; // 不可变借用
    let r2 = &s; // 另一个不可变借用
    let r3 = &s; // 第三个不可变借用
    
    println!("{}, {}, {}", r1, r2, r3); // 可以同时使用多个不可变借用
}
```
</UniversalEditor>

### 可变借用

<UniversalEditor title="可变借用" compare={true}>
```rust !! rs
// 可变借用（类似 JavaScript 的可写引用）
fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

fn main() {
    let mut s = String::from("hello");
    change(&mut s); // 传递可变引用
    println!("{}", s); // "hello, world"
    
    // 可变借用的限制
    let mut s = String::from("hello");
    
    let r1 = &mut s; // 第一个可变借用
    // let r2 = &mut s; // 错误：不能同时有多个可变借用
    // let r3 = &s; // 错误：不能同时有可变和不可变借用
    
    println!("{}", r1); // 使用 r1
    // r1 在这里离开作用域
    
    let r2 = &mut s; // 现在可以创建第二个可变借用
    // let r3 = &s; // 错误：不能同时有可变和不可变借用，需要等待 r2 生命周期结束
    println!("{}", r2);
    let r3 = &s; // 正确：现在可以创建不可变借用
    println!("{}", r3);
}
```
</UniversalEditor>

### 借用规则

<UniversalEditor title="借用规则" compare={true}>
```rust !! rs
// 借用规则总结
fn borrowing_rules() {
    let mut data = vec![1, 2, 3, 4, 5];
    
    // 规则 1: 同一时间只能有一个可变引用或多个不可变引用
    let ref1 = &data; // 不可变引用
    let ref2 = &data; // 另一个不可变引用
    // let ref3 = &mut data; // 错误：不能同时有可变和不可变引用
    
    println!("{:?}, {:?}", ref1, ref2); // 使用不可变引用
    
    // 不可变引用离开作用域后，可以创建可变引用
    let ref3 = &mut data; // 现在可以创建可变引用
    ref3.push(6);
    println!("{:?}", ref3);
    
    // 规则 2: 引用必须总是有效的（不能有悬垂引用）
    // 这个规则在 Rust 中通过生命周期系统自动处理
}

// 悬垂引用的例子（这会导致编译错误）
/*
fn dangle() -> &String {
    let s = String::from("hello");
    &s // 错误：返回对局部变量的引用
} // s 离开作用域，内存被释放，但返回了对它的引用
*/

// 正确的做法
fn no_dangle() -> String {
    let s = String::from("hello");
    s // 返回所有权，而不是引用
}

fn main() {
    borrowing_rules();
    
    let s = no_dangle();
    println!("{}", s);
}
```
</UniversalEditor>

---

## 📏 生命周期

### 生命周期注解

<UniversalEditor title="生命周期" compare={true}>
```rust !! rs
// 生命周期注解
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

fn main() {
    let string1 = String::from("abcd");
    let string2 = "xyz";
    
    let result = longest(string1.as_str(), string2);
    println!("最长的字符串是 {}", result);
    
    // 生命周期注解确保返回的引用在参数的生命周期内有效
}

// 结构体中的生命周期
struct ImportantExcerpt<'a> {
    part: &'a str,
}

fn main() {
    let novel = String::from("Call me Ishmael. Some years ago...");
    let first_sentence = novel.split('.').next().unwrap();
    let i = ImportantExcerpt {
        part: first_sentence,
    };
    
    println!("{}", i.part);
}

// 生命周期省略规则
fn first_word(s: &str) -> &str {
    let bytes = s.as_bytes();
    
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }
    
    &s[..]
}

// 上面的函数等同于：
fn first_word_explicit<'a>(s: &'a str) -> &'a str {
    // 函数体相同
    s
}
```
</UniversalEditor>

---

## 🎯 所有权与 JavaScript 对比

### 数据传递对比

<UniversalEditor title="数据传递对比" compare={true}>
```javascript !! js
// JavaScript 中的对象传递
function modifyUser(user) {
    user.age += 1; // 直接修改原对象
    return user;
}

let user = { name: "Alice", age: 25 };
let modifiedUser = modifyUser(user);

console.log(user.age); // 26 - 原对象被修改
console.log(modifiedUser.age); // 26 - 同一个对象
console.log(user === modifiedUser); // true - 同一个引用

// 数组传递
function addToArray(arr) {
    arr.push("new item");
    return arr;
}

let numbers = [1, 2, 3];
let newNumbers = addToArray(numbers);

console.log(numbers); // [1, 2, 3, "new item"]
console.log(newNumbers); // [1, 2, 3, "new item"]
console.log(numbers === newNumbers); // true
```

```rust !! rs
// Rust 中的所有权转移
fn modify_user(mut user: User) -> User {
    user.age += 1; // 修改结构体
    user
}

fn main() {
    let user = User {
        name: String::from("Alice"),
        age: 25,
    };
    
    let modified_user = modify_user(user);
    // println!("{}", user.age); // 错误：user 已经被移动
    
    println!("{}", modified_user.age); // 26
}

// 使用引用避免所有权转移
fn modify_user_ref(user: &mut User) {
    user.age += 1; // 通过引用修改
}

fn main() {
    let mut user = User {
        name: String::from("Alice"),
        age: 25,
    };
    
    modify_user_ref(&mut user);
    println!("{}", user.age); // 26 - user 仍然有效
}

// 向量传递
fn add_to_vector(mut vec: Vec<i32>) -> Vec<i32> {
    vec.push(4);
    vec
}

fn main() {
    let numbers = vec![1, 2, 3];
    let new_numbers = add_to_vector(numbers);
    // println!("{:?}", numbers); // 错误：numbers 已经被移动
    println!("{:?}", new_numbers); // [1, 2, 3, 4]
}

// 使用引用
fn add_to_vector_ref(vec: &mut Vec<i32>) {
    vec.push(4);
}

fn main() {
    let mut numbers = vec![1, 2, 3];
    add_to_vector_ref(&mut numbers);
    println!("{:?}", numbers); // [1, 2, 3, 4]
}
```
</UniversalEditor>

---

## 🎯 练习题

### 练习 1: 所有权转移

分析以下代码，指出哪些地方会发生所有权转移：

```rust
fn main() {
    let s1 = String::from("hello");
    let s2 = s1;
    let s3 = s2.clone();
    
    println!("s2: {}", s2);
    println!("s3: {}", s3);
}
```

<details>
<summary>查看答案</summary>

1. `let s2 = s1;` - s1 的所有权移动到 s2
2. `let s3 = s2.clone();` - s2 被克隆，s3 获得新数据的所有权
3. s2 仍然有效，因为 clone 创建了新的数据

</details>

### 练习 2: 借用规则

修复以下代码中的借用错误：

```rust
fn main() {
    let mut data = vec![1, 2, 3];
    let ref1 = &data;
    let ref2 = &mut data;
    println!("{:?}, {:?}", ref1, ref2);
}
```

<details>
<summary>查看答案</summary>

```rust
fn main() {
    let mut data = vec![1, 2, 3];
    let ref1 = &data;
    let ref2 = &data; // 改为不可变引用
    println!("{:?}, {:?}", ref1, ref2);
    
    // 或者先使用不可变引用，再使用可变引用
    let mut data = vec![1, 2, 3];
    let ref1 = &data;
    println!("{:?}", ref1);
    let ref2 = &mut data; // 现在可以使用可变引用
    ref2.push(4);
    println!("{:?}", ref2);
}
```

</details>

### 练习 3: 生命周期

为以下函数添加正确的生命周期注解：

```rust
fn longest(x: &str, y: &str) -> &str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}
```

<details>
<summary>查看答案</summary>

```rust
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}
```

</details>

---

## 📝 总结

在这一章中，我们深入学习了 Rust 的所有权系统：

1. **所有权规则**: 每个值只有一个所有者，所有者离开作用域时值被丢弃
2. **借用系统**: 通过引用借用数据，避免所有权转移
3. **借用规则**: 同一时间只能有一个可变引用或多个不可变引用
4. **生命周期**: 确保引用在有效期内使用
5. **与 JavaScript 对比**: Rust 编译时检查 vs JavaScript 运行时垃圾回收

### 关键要点

- 所有权系统是 Rust 内存安全的核心
- 借用系统允许在不转移所有权的情况下使用数据
- 生命周期系统确保引用始终有效
- 这些概念在编译时检查，运行时零开销

### 常见陷阱

1. **移动后使用**: 值被移动后不能再使用
2. **同时借用**: 不能同时有可变和不可变借用
3. **悬垂引用**: 返回对局部变量的引用
4. **生命周期不匹配**: 引用生命周期不匹配

### 下一步学习

在下一章中，我们将学习 Rust 的并发和异步编程模型，了解如何安全地处理多线程和异步操作。

---

**继续学习**: [并发与异步模型](./module-04-concurrency-async) 